Sobre os dados

Focos de queimada

Os dados de focos foram obtidos do banco de dados do INPE: https://queimadas.dgi.inpe.br/queimadas/bdqueimadas#exportar-dados

Os dados foram filtrados pelos limites Amazônia legal, entre os anos de 2011 e 2020.

Outras variáveis

A distância à rodovia mais próxima foi calculada em função de um shapefile de rodovias disponibilizado pelo IBGE. O mapa de grau de urbanização também foi obtido do IBGE. Os dados de áreas desmatadas são elaborados pelo INPE através do PRODES.

Imports

Os pacotes sf e terra são essenciais para a manipulação de dados vetoriais e matriciais respectivamente. O pacote tidyverse contém funções uteis para a manipulação de tabelas. O pacote spatstat é utilizado para extrair estatisticas espaciais como a densidade de focos. O pacote caret é responsável pelo treinamento e aplicação dos modelos.

library(terra) 
library(tidyverse)
library(sf)
library(spatstat)
library(caret)
library(doParallel)

Criação da camada focos/km²

A ideia geral aqui é criar uma imagem raster com a contagem da ocorrência de focos por km². Primeiro os dados de foco devem ser carregados. Eu aproveito para ler o shapefile da amazônia legal também.

focos <- st_read("focos_2011_2020.shp")
## Reading layer `focos_2011_2020' from data source 
##   `F:\Pablo\Documentos\Doutorado\side\focos_2011_2020.shp' using driver `ESRI Shapefile'
## Simple feature collection with 1215884 features and 15 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 2828897 ymin: 8004499 xmax: 6111664 ymax: 10573720
## Projected CRS: SIRGAS 2000 / Brazil Polyconic
am_legal <- st_read("amazonia_legal.shp")
## Reading layer `amazonia_legal' from data source 
##   `F:\Pablo\Documentos\Doutorado\side\amazonia_legal.shp' using driver `ESRI Shapefile'
## Simple feature collection with 1 feature and 1 field
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 2794537 ymin: 8004219 xmax: 6112012 ymax: 10586380
## Projected CRS: SIRGAS 2000 / Brazil Polyconic

Em seguida criar um raster vazio de resolução de 10km para preencher os dados:

focos_km2 <- rast(ext=ext(focos), crs=crs(focos), resolution=10000)

E enfim contar os focos por pixel da imagem:

focos_km2 <- rasterize(vect(focos), focos_km2, fun=length, field=1, background=0) %>%
  mask(vect(am_legal))
colorPal <- colorRampPalette(rev(RColorBrewer::brewer.pal(11, "RdYlGn")))(256)
plot(main="Focos brutos por 10km²", focos_km2, col=colorPal)

Ou então fazer uma análise de densidade:

pts <- st_coordinates(focos) %>%
  unique()
pts_ppp <- ppp(pts[,1], pts[,2], window=as.owin(am_legal), check=FALSE)

A densidade é diferente da quantidade bruta de focos por pixel no sentido de que ela é suavizada em função de uma banda (distância). A função padrão de suavização é a gaussiana.

Um teste rapido usando diferentes bandas:

bandas <- c(10000, 25000, 50000, 100000)
dens <- lapply(bandas, function(x) rast(density(pts_ppp, sigma=x))*1e6)
dens <- rast(dens)
names(dens) <- c("10km", "25km", "50km", "100km")
plot(dens, col=colorPal)

Nesse caso uma banda de 50km pareceu apropriada.

pt_dens <- density(pts_ppp, sigma=50000, eps=10000)
pt_dens <- rast(pt_dens)*1e6
crs(pt_dens) <- crs(focos_km2)
pt_dens <- resample(pt_dens, focos_km2)
plot(main="Densidade de focos por km²", pt_dens, col=colorPal)

Lado a lado:

r_stack <- rast(list(focos_km2, pt_dens))
names(r_stack) <- c("Focos por 10km²", "Densidade de focos")
plot(r_stack, col=colorPal)

É importante lembrar que os valores calculados representam um histórico de 10 anos, portanto o valor anual seria a média, ou o valor apresentado dividido por 10.

Finalmente, salvar os resultados:

writeRaster(focos_km2, "focos_10km.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=T)
writeRaster(pt_dens, "densidade.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=T)

Variáveis independentes

Os mapas dessas variáveis ja foi feito usando o QGIS, então é só carregar eles.

dist_rod <- rast("dist_rod.tif") %>%
  mask(vect(am_legal))
plot(main="Distância à rodovia mais próxima (km)", dist_rod/1000, col=viridis::viridis(256))

dist_desm <- rast("dist_desm.tif") %>%
  mask(vect(am_legal))
plot(main="Distância à área desmatada mais próxima (km)", dist_desm/1000, col=viridis::viridis(256))

grau_urb <- rast("grau_urb.tif") %>%
  mask(vect(am_legal))
plot(grau_urb, main="Grau de urbanização", col=viridis::viridis(256))

Amostragem

A princípio a amostragem vai ser feita por via de pontos aleatórios. Por enquanto vamos usar 5000 pontos:

set.seed(1)
amostras <- st_sample(am_legal, 5000) %>%
  st_sf('ID' = seq(length(.)), 'geometry' = .)
ggplot()+
  geom_sf(data=am_legal, col="gray", fill=NA)+
  geom_sf(data=amostras, col="red", pch= 3, alpha=0.3)+
  theme_bw()

Extração dos dados rasteriais e conversão para tabela:

amostras <- vect(amostras)
dados <- tibble(
  focos_10km2=terra::extract(focos_km2, amostras)[,2],
  densidade=terra::extract(pt_dens, amostras)[,2],
  dist_desm=terra::extract(dist_desm, amostras)[,2],
  dist_rod=terra::extract(dist_rod, amostras)[,2],
  grau_urb=terra::extract(grau_urb, amostras)[,2]
) %>%
  drop_na(densidade)
dados

Traduzindo em forma de gráfico:

ggplot(dados, aes(dist_rod/1000, densidade))+
  geom_point(alpha=0.3)+
  ylab("Focos por km²")+
  xlab("Distância à rodovia (km)")

ggplot(dados, aes(dist_desm/1000, densidade))+
  geom_point(alpha=0.3)+
  ylab("Focos por km²")+
  xlab("Distância ao desmatamento (km)")

ggplot(dados, aes(grau_urb, densidade))+
  geom_point(alpha=0.3)+
  ylab("Focos por km²")+
  xlab("Grau de urbanização")

Regressão

Para começar, um teste usando RandomForest para fazer uma regressão:

cl <- makePSOCKcluster(5)
registerDoParallel(cl)
modelo_rf <- dados %>% 
  train(densidade ~ dist_rod + dist_desm + grau_urb, data=.)
## note: only 2 unique complexity parameters in default grid. Truncating the grid to 2 .
stopCluster(cl)

modelo_rf
## Random Forest 
## 
## 4955 samples
##    3 predictor
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 4955, 4955, 4955, 4955, 4955, 4955, ... 
## Resampling results across tuning parameters:
## 
##   mtry  RMSE       Rsquared   MAE       
##   2     0.1443886  0.5798548  0.09584884
##   3     0.1408890  0.6000214  0.09242855
## 
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was mtry = 3.
dados2 <- dados %>%
  mutate(preds = predict(modelo_rf, newdata=.), erro=(densidade-preds))

Comparação com a realidade:

dados2 %>%
  ggplot(aes(dist_rod/1000, densidade))+
    geom_point(aes(color="Observado"),alpha=0.3)+
    geom_point(aes(y=preds, color="Previsto"), alpha=0.3)+
    xlab("Distância à rodovia")+
    ylab("Densidade")

dados2 %>%
  ggplot(aes(dist_desm/1000, densidade))+
    geom_point(aes(color="Observado"),alpha=0.3)+
    geom_point(aes(y=preds, color="Previsto"), alpha=0.3)+
    xlab("Distância ao desmatamento")+
    ylab("Densidade")

dados2 %>%
  ggplot(aes(grau_urb, densidade))+
    geom_point(aes(color="Observado"),alpha=0.3)+
    geom_point(aes(y=preds, color="Previsto"), alpha=0.3)+
    xlab("Grau de urbanização")+
    ylab("Densidade")

Distribuição de erros:

dados2 %>%
  ggplot(aes(x=erro))+
  geom_histogram(bins=50)+
  scale_x_continuous(breaks=seq(-1,1,0.1))

Enfim classificar a imagem:

dados_stack <- rast(list(dist_desm, resample(dist_rod, dist_desm), resample(grau_urb, dist_desm)))
pred_rast <- predict(dados_stack, modelo_rf, na.rm=T, cores=2)
plot(pred_rast, col=colorPal)

par(mfrow=c(1,2))
plot(main="Previsto", pred_rast, col=colorPal)
plot(main="Observado", pt_dens, col=colorPal)

Alguns pontos onde é possível melhorar: